import elementPlus from './element-plus';
import antDesign from './ant-design';
import arcoDesign from './arco-design';
import tDesign from './t-design';
import { YForm, YNodes } from '../interface';

interface YMapping {
  componentMapping?: { [key: string]: any };
  emitMapping?: { [key: string]: string[] };
  placeholderMapping?: { [key: string]: (label: string) => string | string[] };
  modelMapping?: { [key: string]: string };
  defaultValueMapping?: { [key: string]: any };
  defaultRequiredRuleMapping?: {
    [key: string]: (
      config: { [key: string]: any },
      message: string | string[]
    ) => { [key: string]: any }[];
  };
  formMapping?: {
    model?: string;
    prop?: string;
    linkPath?: string;
  };
}
declare const RawSymbol: unique symbol;
export interface YOptions {
  components?: { [key: string]: any };
  markRaw?: <T extends object>(
    value: T
  ) => T & { [RawSymbol]?: true | undefined };
  mapping?: YMapping;
  version?: string | number;
  methods?: { [key: string]: Function };
  watch?: { [key: string]: any } | undefined;
  computed?: { [key: string]: () => any };
  props?: { [key: string]: any };
  interception?: {
    itemConfig?: { [key: string]: <T>(config: any) => T };
    validate?: (form: any, callback: (isValid: boolean) => void) => void;
    event?: Function;
    get?: (vm: any, prop: string, value: any, config: any) => any;
    set?: (vm: any, prop: string, value: any, config: any) => any;
    props?: (props: { [key: string]: any }, vm: any) => { [key: string]: any };
    rule?: (rule: any, form: any) => any;
    node?: (item: any, form: YForm) => YNodes;
  };
}

export type YUIName = 'elementPlus' | 'antDesign' | 'arcoDesign' | 'tDesign';

function getType(obj: any, str?: string) {
  const typeStr = Object.prototype.toString.call(obj).split(' ')[1];
  const type = typeStr
    .substring(0, typeStr.length - 1)
    .trim()
    .toLowerCase();
  if (
    type === 'object' &&
    Object.prototype.toString.call(obj).toLowerCase() === '[object object]' &&
    !obj.length
  ) {
    return str ? 'json' === str : 'json';
  }
  return str ? type === str : type;
}

const UIMap = { elementPlus, antDesign, arcoDesign, tDesign };
function deepMerge(
  baseObj: { [key: string]: any },
  newObj: { [key: string]: any }
) {
  return Object.keys(newObj || {}).reduce((total, key) => {
    if (key in total) {
      const newType = getType(newObj[key]) as string;
      const baseType = getType(total[key]);
      if (newType === baseType) {
        total[key] =
          ['json', 'array'].indexOf(newType) < 0
            ? newObj[key]
            : newType === 'array'
            ? [...baseObj[key], ...newObj[key]]
            : deepMerge(baseObj[key], newObj[key]);
      } else {
        total[key] = newObj[key];
      }
    } else {
      total[key] = newObj[key];
    }
    return total;
  }, baseObj);
}

function getYcConfigFormOption(UIName: YUIName, options: YOptions = {}) {
  const UIConfig = UIMap[UIName];
  return {
    components: options.components,
    version: options.version,
    mapping: deepMerge(UIConfig.mapping, options.mapping || {}),
    interception: deepMerge(UIConfig.interception, options.interception || {}),
    methods: deepMerge(UIConfig.methods || {}, options.methods || {}),
    computed: deepMerge(UIConfig.computed || {}, options.computed || {}),
    props: deepMerge(UIConfig.props || {}, options.props || {}),
    watch: deepMerge(UIConfig.watch || {}, options.watch || {}),
  };
}

export default getYcConfigFormOption;
